/*-
 * See the file LICENSE for redistribution information.
 *
 * Copyright (c) 2002, 2011 Oracle and/or its affiliates.  All rights reserved.
 *
 * $Id: Trigger.java,v b5a96d37a17d 2011/09/16 03:57:06 mark $
 */
package com.sleepycat.je.trigger;

import java.io.Closeable;
import java.io.Serializable;

import com.sleepycat.je.Database;
import com.sleepycat.je.DatabaseConfig;
import com.sleepycat.je.DatabaseEntry;
import com.sleepycat.je.Environment;
import com.sleepycat.je.Transaction;

/**
 * <code>DatabasetTrigger</code> defines the trigger methods associated with a
 * database. They provide a mechanism to track the database definition
 * operations used to manage the lifecycle of the database itself, as well as
 * the record operations used to modify the contents of the database.
 *
 * <p>
 * The following table captures the relationship between the database
 * granularity operations and their associated trigger methods.
 * </p>
 * <table BORDER CELLPADDING=3 CELLSPACING=1 width="50%" align="center">
 * <tr>
 * <td ALIGN=CENTER><b>Database Operation</b></td>
 * <td ALIGN=CENTER><b>Trigger Method</b></td>
 * </tr>
 * <tr>
 * <td>{@link Environment#openDatabase Environment.openDatabase} resulting in
 * the creation of a new primary database. Or the first open of a database for
 * write operations.</td>
 * <td>{@link #open open}</td>
 * </tr>
 * <tr>
 * <td>{@link Database#close Database.close} the close of a database that was
 * opened for write operations.</td>
 * <td>{@link #close close}</td>
 * </tr>
 * <tr>
 * <td>{@link Environment#removeDatabase Environment.removeDatabase}</td>
 * <td>{@link #remove remove}</td>
 * </tr>
 * <tr>
 * <td>{@link Environment#truncateDatabase Environment.truncateDatabase}</td>
 * <td>{@link #truncate truncate}</td>
 * </tr>
 * <tr>
 * <td>{@link Environment#renameDatabase Environment.renameDatabase}</td>
 * <td>{@link #rename truncate}</td>
 * </tr>
 * </table>
 * </p>
 * <p>
 * The trigger methods {@link #put put} and {@link #delete delete} are used to
 * track all record operations on the database.
 * </p>
 * <p>
 * A trigger method takes a transaction as its first argument. If the
 * environment is not transactional, the argument is null. In all other cases,
 * it's a valid transaction ({@link Transaction#isValid() Transaction.isValid}
 * is true) and the trigger can use this transaction to make it's own set of
 * accompanying changes.
 * <p>
 * If the invocation of a trigger results in a runtime exception, the
 * transaction (if one was associated with the method) is invalidated and any
 * subsequent triggers associated with the operation are skipped. It's the
 * caller's responsibility to handle the exception and abort the invalidated
 * transaction. If the exception is thrown during the replay of a transaction
 * on a replica in an HA application, the environment is invalidated and a new
 * environment handle must be created.
 * </p>
 * <p>
 * A Trigger is associated with a database via
 * {@link DatabaseConfig#setTriggers DatabaseConfig.setTriggers}.
 * </p>
 * Note that all subtypes of Trigger must be serializable, because they are
 * stored persistently in the environment.
 */
public interface Trigger extends Serializable, Closeable {

    /**
     * Returns the name associated with the trigger. All the triggers
     * associated with a particular database must have unique names.
     *
     * @return the Trigger's name
     */
    public String getName();

    /**
     * Sets the database name associated with this trigger. The JE trigger
     * mechanism invokes this method to ensure that the trigger knows the name
     * it's associated with across a rename of the database.
     * <p>
     * This method is invoked whenever the Trigger object is also invoked each
     * time the trigger is de-serialized, so that the trigger does not need to
     * store this information as part of it's serialized representation.
     *
     * @param databaseName the name of the database associated with this
     * trigger
     *
     * @return this
     */
    public Trigger setDatabaseName(String databaseName);

    /**
     * Returns the result of the {@link #setDatabaseName(String)} operation.
     *
     * @return the name of the database associated with this trigger
     */
    public String getDatabaseName();

    /* Trigger lifecycle operations. */

    /**
     * The trigger method invoked when this trigger is added to the database.
     * This is the very first trigger method that is invoked and it's invoked
     * exactly once. If the database is replicated, it's invoked once per node
     * on each node.
     * <p>
     * The method may be invoked when the database is first created, or
     * subsequently when a new trigger is added to an existing database. As a
     * result, a call to this trigger is always followed by a call to the
     * {@link #open(Transaction, Environment, boolean) open} trigger method.
     * </p>
     * @param txn the active transaction associated with the operation. The
     * argument is null if the database is not transactional.
     */
    public void addTrigger(Transaction txn);

    /**
     * The trigger method invoked when this trigger is removed from the
     * database, either as a result of opening the database with a different
     * trigger configuration, or because the database it was associated with
     * it has been removed. In the latter case, this trigger method follows
     * the invocation of the {@link #remove} remove trigger. If the transaction
     * is committed, there will be no subsequent trigger method invocations
     * for this trigger.
     *
     * @param txn the active transaction associated with the operation. The
     * argument is null if the database is not transactional.
     */
    public void removeTrigger(Transaction txn);

    /* Database operations */

    /**
     * The trigger method invoked after the open of the first {@link Database}
     * writable handle.
     *
     * A call to the open trigger always precedes any subsequent calls to the
     * {@link #put} and {@link #delete} triggers defined below, since the
     * <code>put</code> and <code>delete</code> operations can only be invoked
     * on a database handle.
     * <p>
     * If the database is replicated, the replay mechanism on a
     * <code>Replica</code> may open and close databases as it replays the
     * replication stream. The maximum number of databases that may be open at
     * any given time and the duration for which they can be left open can be
     * controlled by configuring
     * <code>ReplicationConfig.REPLAY_MAX_OPEN_DB_HANDLES</code> and
     * <code>ReplicationConfig.REPLAY_DB_HANDLE_TIMEOUT</code> respectively.
     *
     * @param txn the active transaction associated with the operation. The
     * argument is null if the operation is not transactional.
     *
     * @param environment a handle to the environment associated with the
     * database being opened. The trigger code must not close the environment
     * handle.
     *
     * @param isNew is true if the database was newly created as a result of
     * the call to {@link Environment#openDatabase}
     *
     * @see Environment#openDatabase
     */
    public void open(Transaction txn, Environment environment, boolean isNew);

    /**
     * The trigger method associated with the close of the last writable
     * {@link Database} handle.
     * <p>
     * If the database is replicated, the replay mechanism on a
     * <code>Replica</code> may open and close databases as it replays the
     * replication stream. The maximum number of databases that may be open at
     * any given time and the duration for which they can be left open can be
     * controlled by configuring
     * <code>ReplicationConfig.REPLAY_MAX_OPEN_DB_HANDLES</code> and
     * <code>ReplicationConfig.REPLAY_DB_HANDLE_TIMEOUT</code> respectively.
     * <p>
     * @see Database#close
     */
    public void close();

    /**
     * The trigger method invoked after the successful removal of a primary
     * {@link Database}.
     *
     * @param txn the transaction associated with the operation. The argument
     * is null if the environment is non-transactional.
     *
     * @see Environment#removeDatabase
     */
    public void remove(Transaction txn);

    /**
     * The trigger method invoked after the successful truncation of a
     * {@link Database}.
     *
     * @param txn the transaction associated with the operation. The argument
     * is null if the environment is non-transactional.
     *
     * @see Environment#truncateDatabase
     */
    public void truncate(Transaction txn);

    /**
     * The trigger method invoked after the successful renaming of a primary
     * {@link Database}.
     *
     * @param txn the transaction associated with the operation. The argument
     * is null if the environment is non-transactional.
     *
     * @param newName it's current (new) name
     *
     * @see Environment#renameDatabase
     */
    public void rename(Transaction txn, String newName);

    /* Record operations. */

    /**
     * The trigger method invoked after a successful put, that is, one that
     * actually results in a modification to the database.
     * <p>
     * If a new entry was inserted, oldData will be null and newData will be
     * non-null. If an existing entry was updated, oldData and newData will
     * be non-null.
     * </p>
     *
     * @param txn the active transaction associated with the operation. The
     * argument is null if the database is non-transactional.
     *
     * @param key the non-null primary key
     *
     * @param oldData the data before the change, or null if the record
     * did not exist.
     *
     * @param newData the non-null data after the change
     */
    public void put(Transaction txn,
                    DatabaseEntry key,
                    DatabaseEntry oldData,
                    DatabaseEntry newData);
    // TODO: make API provisions for put triggers where we optimize it not to
    // fetch the oldData

    /**
     * The trigger method invoked after a successful delete, that is, one that
     * actually resulted in a key/value pair being removed.
     * <p>
     * Truncating a database does not invoke this trigger;
     * {@link #truncate} is invoked upon truncation.
     * </p>
     *
     * @param txn the active transaction associated with the operation. The
     * argument is null if the database is non-transactional.
     *
     * @param key the non-null primary key
     *
     * @param oldData the non-null data that was associated with the deleted
     * key
     */
    public void delete(Transaction txn,
                       DatabaseEntry key,
                       DatabaseEntry oldData);
}
